/*
 * INCLUDE FILES
 ****************************************************************************************
 */
#include <stdio.h>
#include <string.h>

#include "app_config.h"
#include "gap_api.h"
#include "gatt_api.h"
#include "ble_stack.h"

#include "os_timer.h"
#include "os_mem.h"
#include "sys_utils.h"
#include "jump_table.h"

#include "co_printf.h"
#include "co_log.h"

#include "driver_system.h"
#include "driver_pmu.h"
#include "driver_uart_ex.h"
#include "driver_gpio.h"
#include "driver_flash.h"
#include "driver_efuse.h"
#include "flash_usage_config.h"

#include "plf.h"
#include "at_gap_event.h"
#include "at_cmd_task.h"
#include "at_recv_cmd.h"

#include "at_profile_spss.h"
#include "at_profile_spsc.h"
#include "ota_service.h"

#include "dev_info_service.h"
#include "batt_service.h"
#include "hid_service.h"
#include "driver_uart.h"

#undef LOG_LOCAL_LEVEL
#define LOG_LOCAL_LEVEL        (LOG_LEVEL_INFO)
const char *app_tag = "project";

#define SYSTEM_STACK_SIZE           0x800

void patch_init(void);

uint8_t slave_link_conidx;
uint8_t master_link_conidx;
uint8_t tick = 1;

UART_HandleTypeDef Uart1_handle;
UART_HandleTypeDef Uart0_handle;
static os_timer_t button_anti_shake_timer;  //Timer for button anti-sahke
__attribute__((section("stack_section"))) static uint32_t system_stack[SYSTEM_STACK_SIZE/sizeof(uint32_t)];


const struct jump_table_version_t _jump_table_version __attribute__((section("jump_table_3"))) = 
{
    .stack_top_address = &system_stack[SYSTEM_STACK_SIZE/sizeof(uint32_t)],
    .firmware_version = 0x00000000,
};

const struct jump_table_image_t _jump_table_image __attribute__((section("jump_table_1"))) =
{
    .image_type = IMAGE_TYPE_APP,
    .image_size = 0x19000,
};


__attribute__((section("ram_code"))) void user_entry_before_sleep_imp(void)
{
    pmu_calibration_stop();

    uart_putc_noint_no_wait(UART0, 's');
    co_delay_100us(1);

    pmu_set_pin_to_PMU(GPIO_PORT_A, (1<<GPIO_BIT_0));
    pmu_set_pin_dir(GPIO_PORT_A, (1<<GPIO_BIT_0), GPIO_DIR_IN);
    pmu_set_pin_pull(GPIO_PORT_A, (1<<GPIO_BIT_0),GPIO_PULL_NONE);
    
    pmu_set_pin_to_PMU(GPIO_PORT_A, (1<<GPIO_BIT_2));
    pmu_set_pin_dir(GPIO_PORT_A, (1<<GPIO_BIT_2), GPIO_DIR_IN);
    pmu_set_pin_pull(GPIO_PORT_A, (1<<GPIO_BIT_2),GPIO_PULL_UP);
    pmu_set_pin_to_PMU(GPIO_PORT_A, (1<<GPIO_BIT_3));
    pmu_set_pin_dir(GPIO_PORT_A, (1<<GPIO_BIT_3), GPIO_DIR_IN);
    pmu_set_pin_pull(GPIO_PORT_A, (1<<GPIO_BIT_3),GPIO_PULL_UP);
}

__attribute__((section("ram_code"))) void user_entry_after_sleep_imp(void)
{
    pmu_set_pin_to_CPU(GPIO_PORT_A, (1<<GPIO_BIT_0));

    system_set_port_pull(GPIO_PD4, GPIO_PULL_UP, true);
    system_set_port_mux(GPIO_PORT_A, GPIO_BIT_0, PORTD4_FUNC_UART0_RXD);
    system_set_port_mux(GPIO_PORT_A, GPIO_BIT_1, PORTD5_FUNC_UART0_TXD);
    uart_init(UART0, 1152);

    /* RC calibration start. Ensure the accuracy of sleep wake time */
    pmu_calibration_start(PMU_CALI_SEL_RCLFOSC, LP_RC_CALIB_CNT);

    uart_putc_noint_no_wait(UART0, 'w');
    co_delay_100us(1);

    pmu_set_pin_to_CPU(GPIO_PORT_A, (1<<GPIO_BIT_2));
    pmu_set_pin_to_CPU(GPIO_PORT_A, (1<<GPIO_BIT_3));
    __SYSTEM_UART1_CLK_ENABLE();
	system_set_port_pull(GPIO_PA2,GPIO_PULL_UP,true);
    /* set PA2 and PA3 for AT command interface */
    system_set_port_mux(GPIO_PORT_A, GPIO_BIT_2, PORTA2_FUNC_UART1_RXD);
    system_set_port_mux(GPIO_PORT_A, GPIO_BIT_3, PORTA3_FUNC_UART1_TXD);
    Uart1_handle.UARTx = Uart1;
    Uart1_handle.Init.BaudRate   = gAT_buff_env.uart_param.BaudRate;
    Uart1_handle.Init.DataLength = gAT_buff_env.uart_param.DataLength;
    Uart1_handle.Init.StopBits   = gAT_buff_env.uart_param.StopBits;
    Uart1_handle.Init.Parity     = gAT_buff_env.uart_param.Parity;
    Uart1_handle.Init.FIFO_Mode  = UART_FIFO_ENABLE;

    uart_init_ex(&Uart1_handle);

	/*enable recv and line status interrupt*/
    __UART_INT_LINE_STATUS_ENABLE(Uart1_handle.UARTx);
    __UART_INT_RX_ENABLE(Uart1_handle.UARTx);

	NVIC_EnableIRQ(UART1_IRQn);
    NVIC_SetPriority(UART1_IRQn, 1);

    NVIC_EnableIRQ(PMU_IRQn);
}

/*********************************************************************
 * @fn      button_anti_shake_timeout_handler
 *
 * @brief   Anti-shake-timer execute fuinction.
 *
 * @param   argument for timer function.
 *
 *
 * @return  None.
 */
static void button_anti_shake_timeout_handler(void *param)
{
	co_printf("enter the button_anti_shake_timeout_handler\r\n");
}

void proj_init(void)
{
    LOG_INFO(app_tag,"proj_init\r\n");

    LOG_INFO(app_tag,"lp clk=%d\r\n", pmu_get_rc_clk(false));

    gap_set_cb_func(proj_ble_gap_evt_func);

    at_load_info_from_flash();
    at_init();

    //Set two button, PC5,PD6
    os_timer_init(&button_anti_shake_timer, button_anti_shake_timeout_handler, NULL);
    pmu_set_pin_pull(GPIO_PORT_D, BIT(6), GPIO_PULL_UP);     //PD6, disconnect,
    pmu_set_pin_pull(GPIO_PORT_D, BIT(5), GPIO_PULL_UP);     //PD5 wakeup
	pmu_port_wakeup_func_set(GPIO_PORT_D, BIT(6));
	pmu_port_wakeup_func_set(GPIO_PORT_D, BIT(5));
	
    ADV_LED_INIT;
    LINK_LED_INIT;

    gap_security_param_t param =
    {
        .mitm = false,
        .ble_secure_conn = false,
        .io_cap = GAP_IO_CAP_NO_INPUT_NO_OUTPUT,
        .pair_init_mode = GAP_PAIRING_MODE_WAIT_FOR_REQ,
        .bond_auth = true,
        .password = 0,
    };
    //initialize bonding configuration
    gap_security_param_init(&param);

    at_init_gap_cb_func();
    
    at_init_advertising_parameter();

    //Add AT service and client profile
    at_profile_spsc_init();
    at_profile_spss_init();

    //Add OTA, Device info, batt, hid profile
    ota_gatt_add_service();
    //dis_gatt_add_service();
    //batt_gatt_add_service();
    //hid_gatt_add_service();
}

void pmu_gpio_isr(uint32_t pin_value)
{
    if(!(GPIO_PD5 & pin_value))
    {
        system_sleep_disable();
        gAT_buff_env.default_info.auto_sleep = false;
    }
}

__attribute__((section("ram_code"))) void main_loop(void)
{
    while(1)
    {
        if(ble_stack_schedule_allow())
        {
            /*user code should be add here*/
            
            /* schedule internal stack event */
            ble_stack_schedule();
        }
        
        GLOBAL_INT_DISABLE();
        switch(ble_stack_sleep_check())
        {
            case 2:
            {
                ble_stack_enter_sleep();
            }
            break;
            default:
                break;
        }
        GLOBAL_INT_RESTORE();
        
        ble_stack_schedule_backward();
    }
}

/*********************************************************************
 * @fn      user_entry_after_ble_init
 *
 * @brief   User entry function after ble stack initialization.
 *
 * @param   None.
 *
 * @return  None.
 */
void user_main(void)
{
	/* initialize log module */
    log_init();
    
	/* initialize PMU module at the beginning of this program */
    pmu_sub_init();

    patch_init();
    /* set system clock */
    system_set_clock(SYSTEM_CLOCK_SEL);

    /* set local BLE address. use the SOC unique ID as the address */
    mac_addr_t mac_addr;
    system_get_unique_ID(mac_addr.addr);
    mac_addr.addr[5] = 0x20|0xC0;
    gap_address_set(&mac_addr, BLE_ADDR_TYPE_PRIVATE);
    
    /* configure ble stack capabilities */
    ble_stack_configure(BLE_STACK_ENABLE_MESH,
                        BLE_STACK_ENABLE_CONNECTIONS,
                        BLE_STACK_RX_BUFFER_CNT,
                        BLE_STACK_RX_BUFFER_SIZE, 
                        BLE_STACK_TX_BUFFER_CNT, 
                        BLE_STACK_TX_BUFFER_SIZE, 
                        BLE_STACK_ADV_BUFFER_SIZE,
                        BLE_STACK_RETENTION_RAM_SIZE,
                        BLE_STACK_KEY_STORAGE_OFFSET);
    /* initialize ble stack */
    ble_stack_init();
    /* initialize SMP */
	gap_bond_manager_init(BLE_BONDING_INFO_SAVE_ADDR, BLE_REMOTE_SERVICE_SAVE_ADDR, 8, true);
    
    proj_init();

#if 1
    system_sleep_disable();
#endif

    /* enter main loop */
    main_loop();
}
